package cn.lysoy.demo.dataPoolTest;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;

/**
 * 简易数据库连接池
 * @author 宋杨
 * @email s2295938761@163.com
 * @phone 15216839375
 * @date 2022/9/1
 */
public class MyConnectionPool {
    //提供静态的私有的本类对象
    private static MyConnectionPool pool;
    //构造方法私有化
    private MyConnectionPool() {
        init();//在构造方法中初始化连接池
    }
    //提供静态的可以获取本类对象的方法。
    public synchronized static MyConnectionPool getPool() {
        if(pool==null)
            pool = new MyConnectionPool();
        return pool;
    };

    //成员属性
    //数据库连接属性
    private static String driverClass;
    private static String userName;
    private static String url;
    private static String password;
    //初始连接数量
    private static int initCount = 5;
    //最小连接数量
    private static int minCount = 5;
    //最大连接数量
    private static int maxCount = 15;
    //当前的连接数量
    private static int count;
    //使用集合存储连接
    LinkedList<Connection> conns = new LinkedList<>();

    //成员属性初始化
    static {
        Properties properties = new Properties();
        try {
            properties.load(MyConnectionPool.class.getClassLoader().getResourceAsStream("pool/db.properties"));
            driverClass = properties.getProperty("jdbc.driverclass");
            url  = properties.getProperty("jdbc.url");
            userName = properties.getProperty("jdbc.username");
            password = properties.getProperty("jdbc.password");
            try {
                initCount = new Integer(properties.getProperty("jdbc.initCount"));
            } catch (NumberFormatException e) {
                System.out.println("initCount使用默认值："+initCount);
            }
            try {
                minCount = new Integer(properties.getProperty("jdbc.minCount"));
            } catch (NumberFormatException e) {
                System.out.println("minCount使用默认值："+minCount);
            }
            try {
                maxCount = new Integer(properties.getProperty("jdbc.maxCount"));
            } catch (NumberFormatException e) {
                System.out.println("maxCount使用默认值："+maxCount);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 初始化连接池
     */
    private void init() {
        // 循环的给集合中添加初始化个数个链接
        for (int i = 0; i < initCount; i++) {
            boolean flag = conns.add(createCon());
            if(flag) {
                //添加成功之后就将给连接数量+1
                count ++;
            }
        }
        System.out.println("连接池初始化完成");
    }
    /**
     * 创建一个可用的数据库连接
     * @return
     */
    private Connection createCon() {
        try {
            Class.forName(driverClass);
            return DriverManager.getConnection(url, userName, password);
        } catch (Exception e) {
            throw new RuntimeException("链接创建失败："+e.getMessage());
        }
    }

    /**
     * 池中连接自动增长
     */
    private synchronized void addAuto() {
        // 增长步长为2
        if (count == maxCount) {
            throw new RuntimeException("池中数量已经达到最大，不能再增长");
        }
        // 增长过程中，判断池中的个数
        for (int i = 0; i < 2; i++) {
            if (count == maxCount) {
                break;//如果已经到最大值，则直接结束循环
            }
            conns.add(createCon());
            count++;
        }
    }

    /**
     * 从池中获取一个可用的连接
     * @return
     */
    public Connection getCon() {
        //判断池中是否有空闲链接
        if(conns.size()>0) {
            //如果有就直接返回
            return conns.removeFirst();
        }
        //如果池中没有空闲的连接
        //判断总连接数是否已经达到上限
        if(count < maxCount) {
            //让连接池自动增长
            addAuto();
            //自动增长完成之后，递归调用返回连接的方法
            return getCon();
        }
        //如果池中的连接数已经达到上限，就等一会再获取
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return  getCon();
    }

    /**
     * 连接池自动缩减的方法
     */
    private synchronized void reduce() {
        //当连接数量大于最小连接数量，并且池中有空闲连接
        if(count > minCount && conns.size() > 0) {
            try {
                //从池中获取一个链接，并且关闭
                Connection con = conns.removeFirst();
                con.close();
                con = null;
                //如果关闭了一个连接，就将当前的链接数量-1
                count--;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 还回连接到池中
     * @param con
     */
    public void returnCon(Connection con) {
        //将连接直接放入集合
        conns.add(con);
        //连接还回之后，马上缩减连接池
        reduce();
    }

    public void close() {
        while (count > 0) {
            try {
                //从池中获取一个链接，并且关闭
                Connection con = conns.removeFirst();
                con.close();
                con = null;
                //如果关闭了一个连接，就将当前的链接数量-1
                count--;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
